// JSON.stringify() 方法将一个 JavaScript 对象或值转换为 JSON 字符串，如果指定了一个 replacer 函数，则可以选择性地替换值，或者指定的 replacer 是数组，则可选择性地仅包含数组指定的属性。

// JSON.stringify()将值转换为相应的 JSON 格式：

// 转换值如果有 toJSON() 方法，该方法定义什么值将被序列化。
// 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。
// 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
// undefined、任意的函数以及 symbol 值，在序列化过程中会被忽略（出现在非数组对象的属性值中时）或者被转换成 null（出现在数组中时）。函数、undefined 被单独转换时，会返回 undefined，如JSON.stringify(function(){}) or JSON.stringify(undefined).
// 对包含循环引用的对象（对象之间相互引用，形成无限循环）执行此方法，会抛出错误。
// 所有以 symbol 为属性键的属性都会被完全忽略掉，即便 replacer 参数中强制指定包含了它们。
// Date 日期调用了 toJSON() 将其转换为了 string 字符串（同 Date.toISOString()），因此会被当做字符串处理。
// NaN 和 Infinity 格式的数值及 null 都会被当做 null。
// 其他类型的对象，包括 Map/Set/WeakMap/WeakSet，仅会序列化可枚举的属性。
function jsonStringify(data) {
  let dataType = typeof data;
  
  if (dataType !== 'object') {
      let result = data;
      //data 可能是 string/number/null/undefined/boolean
      if (Number.isNaN(data) || data === Infinity) {
          //NaN 和 Infinity 序列化返回 "null"
          result = "null";
      } else if (dataType === 'function' || dataType === 'undefined' || dataType === 'symbol') {
          //function 、undefined 、symbol 序列化返回 undefined
          return undefined;
      } else if (dataType === 'string') {
          result = '"' + data + '"';
      }
      //boolean 返回 String()
      return String(result);
  } else if (dataType === 'object') {
      if (data === null) {
          return "null"
      } else if (data.toJSON && typeof data.toJSON === 'function') {
          return jsonStringify(data.toJSON());
      } else if (data instanceof Array) {
          let result = [];
          //如果是数组
          //toJSON 方法可以存在于原型链中
          data.forEach((item, index) => {
              if (typeof item === 'undefined' || typeof item === 'function' || typeof item === 'symbol') {
                  result[index] = "null";
              } else {
                  result[index] = jsonStringify(item);
              }
          });
          result = "[" + result + "]";
          return result.replace(/'/g, '"');
          
      } else {
          //普通对象
          /**
           * 循环引用抛错(暂未检测，循环引用时，堆栈溢出)
           * symbol key 忽略
           * undefined、函数、symbol 为属性值，被忽略
           */
          let result = [];
          Object.keys(data).forEach((item, index) => {
              if (typeof item !== 'symbol') {
                  //key 如果是symbol对象，忽略
                  if (data[item] !== undefined && typeof data[item] !== 'function'
                      && typeof data[item] !== 'symbol') {
                      //键值如果是 undefined、函数、symbol 为属性值，忽略
                      result.push('"' + item + '"' + ":" + jsonStringify(data[item]));
                  }
              }
          });
          return ("{" + result + "}").replace(/'/g, '"');
      }
  }
}